package logger

import (
	"fmt"
	"os"
	"strings"
	"time"

	"gitee.com/yyzh999/message/internal/pkg/config"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var log *zap.Logger
var sugarLog *zap.SugaredLogger

func init() {
	encoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
		TimeKey:          "ts",
		LevelKey:         "level",
		NameKey:          "logger",
		CallerKey:        "caller",
		FunctionKey:      zapcore.OmitKey,
		MessageKey:       "msg",
		StacktraceKey:    "stacktrace",
		LineEnding:       zapcore.DefaultLineEnding,
		EncodeLevel:      zapcore.LowercaseLevelEncoder,
		EncodeTime:       zapcore.ISO8601TimeEncoder,
		EncodeDuration:   zapcore.NanosDurationEncoder,
		EncodeCaller:     zapcore.ShortCallerEncoder,
		SkipLineEnding:   false,
		ConsoleSeparator: "\t",
	})

	cfgLevel := getCustomLogLevel()
	infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl < zapcore.WarnLevel && lvl >= cfgLevel
	})
	warnLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.WarnLevel && lvl >= cfgLevel
	})

	var core zapcore.Core
	if config.IsOutConsole() {
		console := zapcore.Lock(os.Stdout)
		core = zapcore.NewTee(
			zapcore.NewCore(encoder, getInfoSyncer(), infoLevel),
			zapcore.NewCore(encoder, getWarnSyncer(), warnLevel),
			zapcore.NewCore(encoder, console, infoLevel),
			zapcore.NewCore(encoder, console, warnLevel),
		)
	} else {
		core = zapcore.NewTee(
			zapcore.NewCore(encoder, getInfoSyncer(), infoLevel),
			zapcore.NewCore(encoder, getWarnSyncer(), warnLevel),
		)
	}
	log = zap.New(core, zap.AddCaller(),
		zap.AddCallerSkip(1),
		zap.AddStacktrace(zap.ErrorLevel))

	sugarLog = log.Sugar()
}

func getCustomLogLevel() zapcore.Level {
	logLevel, err := zapcore.ParseLevel(config.GetLoggerLevel())
	if err != nil {
		fmt.Printf("log level parse failed, [%s] - %s", config.GetLoggerLevel(), err.Error())
		panic(err)
	}
	return logLevel
}

func getInfoSyncer() zapcore.WriteSyncer {
	path := getLoggerPath() + config.GetLoggerName()
	rotateLog := setRotateLogs(path, ".log")
	return zapcore.AddSync(rotateLog)
}

func getWarnSyncer() zapcore.WriteSyncer {
	path := getLoggerPath() + config.GetLoggerName()
	rotateLog := setRotateLogs(path, ".wf.log")
	return zapcore.AddSync(rotateLog)
}

func setRotateLogs(path, ext string) *rotatelogs.RotateLogs {
	filePath := path + ext
	rotateLog, err := rotatelogs.New(
		strings.Replace(filePath, ".log", "", -1)+"_%Y%m%d%H%M"+ext,
		rotatelogs.WithMaxAge(7*time.Minute),
		rotatelogs.WithRotationTime(time.Minute),
		rotatelogs.ForceNewFile(),
	)

	if err != nil {
		fmt.Println("log file open failed")
		panic(err)
	}

	return rotateLog
}

func getLoggerPath() string {
	path := config.GetLoggerPath()
	if path == "" {
		path = "./logs"
	}
	fmt.Println(path)
	if !strings.HasSuffix(path, "/") {
		path = path + "/"
	}
	if _, err := os.Stat(path); os.IsNotExist(err) {
		err = os.MkdirAll(path, os.ModePerm)
		if err != nil {
			fmt.Printf("log directory[%s] create failed,%s", path, err.Error())
			panic(err)
		}
	}
	return path
}

func DebugS(args ...interface{}) {
	sugarLog.Debug(args...)
}

func DebugSf(template string, args ...interface{}) {
	sugarLog.Debugf(template, args...)
}

func InfoS(args ...interface{}) {
	sugarLog.Info(args...)
}

func InfoSf(template string, args ...interface{}) {
	sugarLog.Infof(template, args...)
}

func WarnS(args ...interface{}) {
	sugarLog.Warn(args...)
}

func WarnSf(template string, args ...interface{}) {
	sugarLog.Warnf(template, args...)
}

func ErrorS(args ...interface{}) {
	sugarLog.Error(args...)
}

func ErrorSf(template string, args ...interface{}) {
	sugarLog.Errorf(template, args...)
}

func DPanicS(args ...interface{}) {
	sugarLog.DPanic(args...)
}

func DPanicSf(template string, args ...interface{}) {
	sugarLog.Panicf(template, args...)
}

func FatalS(args ...interface{}) {
	sugarLog.Fatal(args...)
}

func FatalSf(template string, args ...interface{}) {
	sugarLog.Fatalf(template, args...)
}

func Debug(msg string, fields ...zapcore.Field) {
	log.Debug(msg, fields...)
}

func Info(msg string, fields ...zapcore.Field) {
	log.Info(msg, fields...)
}

func Warn(msg string, fields ...zapcore.Field) {
	log.Warn(msg, fields...)
}

func Error(msg string, fields ...zapcore.Field) {
	log.Error(msg, fields...)
}

func DPanic(msg string, fields ...zapcore.Field) {
	log.DPanic(msg, fields...)
}

func Panic(msg string, fields ...zapcore.Field) {
	log.Panic(msg, fields...)
}

func Fatal(msg string, fields ...zapcore.Field) {
	log.Fatal(msg, fields...)
}
